//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================

#include <elasys_server.h>
#include <elapi.h>
#include <driver.h>
#include <PowerDef.h>
#include "DevicePower.h"
#include <sysconfig.h>

static Device_Power device_power[MaxDevice];//record information of devices.
static IDeviceDriver  * pBattery = NULL;
int IsPowerManagerNeeded;

extern"C" ECode DM_NotifyInput();
static void Power_NotifyInput(int eType, int nCode, void * pParam)
{
    DM_NotifyInput();
}

int governor(void * arg);
void Power_CallbackTaskChange(int eType, int nCode, void * pParam);

/*the following code is implemented to surport device power management in emulator*/
unsigned GetCurTime()
{
    SystemTime t;
    _CSystem_GetMachineTime(&t);
    return t.seconds;
}

void InitializeDevicePower()
{
    for (int i = 0;i < MaxDevice;i++) {
        device_power[i].Mode=0;
        device_power[i].PowerConsumption=0;
        device_power[i].UpdateTime=GetCurTime();
        device_power[i].TotalEnergyConsumption=0;
    }
    strncpy (device_power[0].Name,"CPU",sizeof("CPU"));
    strncpy (device_power[1].Name,"Audio",sizeof("Audio"));
    strncpy (device_power[2].Name,"Camer",sizeof("Camer"));
    strncpy (device_power[3].Name,"LCD",sizeof("LCD"));
    strncpy (device_power[4].Name,"Key",sizeof("Key"));
    strncpy (device_power[5].Name,"Infr",sizeof("Infr"));
    strncpy (device_power[6].Name,"UART",sizeof("UART"));
    strncpy (device_power[7].Name,"BlueT",sizeof("BlueT"));
    strncpy (device_power[8].Name,"BaseB",sizeof("BaseB"));
    strncpy (device_power[9].Name,"SD",sizeof("SD"));
}

extern"C" int Power_GetDeviceID(wchar_t * Name)
{//maybe changed,it should be united in the system.
    if(wcscmp(Name, L"core:cpu") == 0)return 0;
    if(wcscmp(Name, L"device:wave0") == 0)return 1;
    if(wcscmp(Name, L"device:camera0") == 0)return 2;
    if(wcscmp(Name, L"device:display0") == 0)return 3;
    if(wcscmp(Name, L"device:keypad0") == 0)return 4;
    if(wcscmp(Name, L"dev:IrDA") == 0)return 5;
    if(wcscmp(Name, L"device:serial2") == 0)return 6;
    if(wcscmp(Name, L"dev:bluetooth") == 0)return 7;
    if(wcscmp(Name, L"dev:baseband") == 0)return 8;
    if(wcscmp(Name, L"device:touch") == 0)return 9;
    return -1;
}

unsigned long Power_GetDevicePowerLevel(int DeviceID, unsigned long Mode)
{//it should be designed carefully later.
    if(DeviceID < 0 || DeviceID > MaxDevice)return 0x0;
    switch (DeviceID) {
        case 0:return Scalar_GetCpuPower(Mode);//compute in scale
        case 1:{//for audio
            switch (Mode) {
                case 0:return 0;
                case 1:return 0;
                case 2:return 0;//20 mw
                case 3:return 50;
                case 4:return 50;
                default:return 50;
            }
        }
        case 2:{//for camera
            switch (Mode) {
                case 0:return 0;
                case 1:return 100;
                case 2:return 150;//200 mw
                case 3:return 200;//200 mw
                case 4:return 250;//200 mw
                default:return 250;
            }
        }
        case 3:{//for lcd
            switch (Mode) {
                case 0:return 0;
                case 1:return 100;
                case 2:return 200;//200mw,the normal level
                case 3:return 250;
                case 4:return 300;//100 mw
                default:return 200;
            }
        }
        case 4:break;
        case 5:break;
        case 6:break;
        case 7:break;
        case 8:break;
        case 9:break;
        default :break;
    }
    return 0;
}

ECode Battery_Initialize()
{
    ECode ec;
    ec = _CSystem_FindRunningObject(
            L"device:battery", (PInterface *)&pBattery);
    if (FAILED(ec)) {
        return ec;
    }
    assert(pBattery);
    return ec;
}

void Battery_Consume(unsigned energy)
{
    MemoryBuf_<4> ebbint;
    ebbint.Copy((Byte *)&energy, sizeof(int));
    pBattery->Control(0,ebbint, NULL, NULL );
}

EXTERN_C void Power_UpdateDevicePower(int DeviceID, unsigned long Mode)
{
    if(DeviceID < 0 || DeviceID > MaxDevice)return;
    //maybe need some synchonrized mechanism,but now we don't render.
    unsigned cur_time=GetCurTime();
    device_power[DeviceID].Mode=Mode;
    unsigned long power=Power_GetDevicePowerLevel(DeviceID,Mode);
    device_power[DeviceID].PowerConsumption=power;
    unsigned interval=cur_time-device_power[DeviceID].UpdateTime;
    device_power[DeviceID].UpdateTime=cur_time;
    device_power[DeviceID].TotalEnergyConsumption+=interval*power;//note:power's unit is mw and for time the unit is s.so the unit for energy is mj
    Battery_Consume(device_power[DeviceID].TotalEnergyConsumption);//update battery consumption.
    Scalar_SendDevicePower(&device_power[DeviceID],DeviceID);//send to display.
}

/////////////////////////////////////////////////////////////////////
EXTERN_C bool IsPowerManNeeded()
{
    IsPowerManagerNeeded=0;
    ECode ec = ConfigGetItemInt(L"powermanager", &IsPowerManagerNeeded);
    if(FAILED(ec) || !IsPowerManagerNeeded)return false;
    return true;
}

extern IMutex * pmutex;
extern IEvent * pevent;

EXTERN_C int  Power_Initialize()
{
    if(!IsPowerManNeeded())return -1;
    _SysSetHook(31, (void *)Power_CallbackTaskChange);
    _SysSetHook(30, (void *)Power_NotifyInput);

    _CMutex_New(&pmutex);
    _CEvent_New(FALSE,0,&pevent);

    ECode ec=Battery_Initialize();
    if(FAILED(ec))return -1;
    ec=_CThread_New((PThreadMain)governor,NULL,0,NULL);
    if(FAILED(ec))return -1;
    InitializeDevicePower();
    Scalar_Initialize();
    return 0;
}

bool RunFullSpeed()
{
    int IsFullSpeed=0;
    ECode ec = ConfigGetItemInt(L"fullspeed", &IsFullSpeed);
    if (FAILED(ec) || IsFullSpeed)
        return 1;
    else
        return 0;
}
